// block_queue.h
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
#include <cassert>

template<typename T>
class block_queue {
public:
    block_queue(int max_queue_size = 1000)
      : m_max_size(max_queue_size), m_is_closed(false)
    { assert(max_queue_size > 0); }

    ~block_queue() {
      std::lock_guard<std::mutex> lock(m_mutex);
      m_is_closed = true;
      m_not_empty.notify_all();
      m_not_full.notify_all();
    }

    void push(const T& item) {
      std::unique_lock<std::mutex> lock(m_mutex);
      m_not_full.wait(lock, [this]{ return m_queue.size() < m_max_size || m_is_closed; });
      if (m_is_closed) return;
      m_queue.push(item);
      m_not_empty.notify_one();
    }

    bool pop(T& item) {
      std::unique_lock<std::mutex> lock(m_mutex);
      m_not_empty.wait(lock, [this]{ return !m_queue.empty() || m_is_closed; });
      if (m_queue.empty()) return false;
      item = m_queue.front();
      m_queue.pop();
      m_not_full.notify_one();
      return true;
    }

    bool full() {
      std::lock_guard<std::mutex> lock(m_mutex);
      return m_queue.size() >= m_max_size;
    }

    bool empty() {
      std::lock_guard<std::mutex> lock(m_mutex);
      return m_queue.empty();
    }

    size_t size() {
      std::lock_guard<std::mutex> lock(m_mutex);
      return m_queue.size();
    }

    size_t capacity() {
      return m_max_size;
    }

private:
    std::queue<T> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_not_empty;
    std::condition_variable m_not_full;
    size_t m_max_size;
    bool m_is_closed;
};
